Skip to content

W-886: Digit quick-pick for the typed-query list - #249

Open
Aryan-Techie wants to merge 2 commits into
wr:mainfrom
Aryan-Techie:w-886-list-digit-quick-pick
Open

Aryan-Techie wants to merge 2 commits into
wr:mainfrom
Aryan-Techie:w-886-list-digit-quick-pick

Conversation

@Aryan-Techie

Copy link
Copy Markdown

Summary

While the shortcode list is up (:smil), pressing a digit inserts that row, the same way 1–8 already works on the Quick Access pill. Each pickable row shows its digit at the trailing edge, like a menu's key equivalents, and the footer reads ↑↓ ↵ 1–6 insert (1–N when fewer rows match).

Digits cover the rows the list draws without scrolling. That's 6 today (PickerLayout.maxVisibleRows), not the 9 the issue suggests, so a digit never inserts a row you can't see. The state machine allows up to 9, so raising maxVisibleRows extends it.

The collision guard is the one proposed in the issue: a digit picks only when appending it would leave the query with no matches. So :smil + 2 picks the second row, while :clock1 (🕐), :100: (💯) and :+1: still type normally. 0 never picks. A sticky session (Shift+Return) keeps typing digits into its search, and the trailing "Browse all emojis…" row is never a target.

  • KeyMonitor/TriggerStateMachine.swift: new .pickListIndex(_:query:scope:) action. A digit picks when listQuickPickCount (the rows the Engine says are on screen) covers it and the Engine-supplied queryHasMatches probe reports that the extended query matches nothing. Any query edit, sticky/browser entry, new capture or reset() disarms the count until the next refresh, so a digit never picks from a stale list. Mid-close (::star:) digits type through. The pill path (emptyPickerActive / .pickIndex) is untouched.
  • App/Engine.swift: arms the count from the visible rows it puts in the list, and only during a live capture. The probe is a FuzzyMatcher.search(limit: 1) that runs only when a digit arrives while the list is armed. .pickListIndex goes through the same insert(.fromPicker) path as Return, so the erased span (:query, ::query, or a custom open) matches Return exactly. A digit is never consumed without a row to insert. The probe runs inside stateMachine.handle, so it reads a mirrored copy of the trigger config instead of stateMachine.config, which would be an overlapping access and trap Swift's runtime exclusivity check.
  • Picker/PickerView.swift, PickerViewModel.swift, HintsFooter.swift: row badges and the footer hint. KeyHint gains a multi-key form so ↵ and 1–9 share the "insert" label. To fit the 280 pt panel, the arrows drop their "select" label while digits are live; sticky sessions keep the old footer. No new strings.
  • Tests/MojitoTests/TriggerStateMachineTests.swift: ListQuickPickTests (13 tests). They cover picking, the collision guard (and what the probe receives), 0, out-of-range digits, unarmed and probe-less states, disarm on edit, :: scope delete length, sticky sessions, the unchanged pill pick, and reset().

Offscreen renders of PickerView (no panel glass):

Light Dark

Test plan

  • Full MojitoTests suite passes locally: 336 tests, built against the macOS 26.5 SDK with the Swift 6.4 command-line tools through a SwiftPM wrapper, because the machine I built this on has no Xcode. The one exception, DebugReportTests.forbiddenStringsNeverAppear, fails the same way on main under that wrapper, since the report sees the runner's non-bundle path.
  • scripts/check_localizations.py and scripts/check_telemetry.py pass.
  • scripts/run-tests.sh / CI on Xcode.
  • In Mojito Dev: :smil then 2 inserts the second row and erases :smil; :clock1 keeps typing; ::star then 1 inserts a symbol and erases ::star; after Shift+Return, digits search in the sticky session; :? then 3 still picks from the pill.

Fixes #204 · Refs W-886

While the shortcode list is up, 1–9 inserts that row, the way 1–8
already does on the Quick Access pill. The first nine rows show their
digit at the trailing edge, and the footer shows a `1–9` keycap next to
↵ (the arrows lose their "select" label to make room in the 280 pt
panel).

A digit only picks when appending it would leave the query with no
matches, so `:smil` + `2` picks the second row while `:clock1`, `:+1:`
and `:100:` still type normally. The state machine asks the Engine
through a search probe that runs only on a digit while the list is
armed. The Engine arms the row count from the results it shows, and any
query edit disarms it until the next refresh, so a digit never picks
from a stale list. The trailing Browse row is never a target, and a
sticky (Shift+Return) session keeps typing digits into its search.

Refs: W-886
The collision probe runs inside `stateMachine.handle`, a mutating call
on an Engine property, and it read `stateMachine.config` through
`corpusFor`. That overlapping access fails Swift's runtime exclusivity
check, so the first digit typed with the list armed (`:smil` + `1`)
would trap. Engine now mirrors the trigger config it loads, and
`corpusFor` reads that copy.

Digits also stop at the rows the list draws without scrolling
(`PickerLayout.maxVisibleRows`, 6), so a digit never inserts a row the
user can't see; the footer reads `1–6`.

Refs: W-886

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Digit quick-pick (1–9) for the typed-query picker list, like numbered suggestions in Cotypist

1 participant